/** 
    @file pixellayer.hpp
    @brief Game map layer
*/

#ifndef PM3_PIXELLAYER_HPP
#define PM3_PIXELLAYER_HPP

#include "lua/lua_scripted.hpp"
#include "pixelroom.hpp"

#include <OgreImage.h>

namespace pm3 {
    /**
        @class IMAGE
        @brief Ogre::Image with a few addons
    */
    class IMAGE: public Ogre::Image {
        public:
            IMAGE() {}
            ~IMAGE() {}

            /**
                @brief Sets pixel color at a certain point
                @param[in] x X-coordinate of the pixel
                @param[in] y Y-coordinate of the pixel
                @param[in] z Z-coordinate of the pixel (only valid for cubemaps and volumetric textures)
                @param[in] r_color Reference to the color to be set
            */
            void setColourAt(int x, int y, int z, const Ogre::ColourValue &r_color);

            /**
                @brief Resizes the image
                @param[in] x X-coordinate of the old image content
                @param[in] y Y-coordinate of the old image content
                @param[in] width New image width
                @param[in] height New image height
            */
            void resize(int x, int y, int width, int height);
    };

    /**
        @class PIXELLAYER
        @brief Game map layer
    */
    class PIXELLAYER: public LUA_SCRIPTED {
    friend class PIXELMAP;
    public:
            /**
                @brief Constructor
            */
            PIXELLAYER();
            /**
                @brief Destructor
            */
            ~PIXELLAYER();

            /**
                @brief Assignment operator
            */
            PIXELLAYER& operator=( const PIXELLAYER &r_layer );

            /**
                @brief Unloads the layer
            */
            void unload();

            /**
                @brief Gets a pointer to a room on the map
                @param[in] i_x X-coordinate of the room position
                @param[in] i_y Y-coordinate of the room position
                @return Pointer to the room or NULL in case there is none
            */
            PIXELROOM *get_room( int i_x, int i_y );

            /**
                @brief Gets the color of a pixel
                @param[in] i_x X-coordinate of the pixel position
                @param[in] i_y Y-coordinate of the pixel position
                @return Pixel color
            */
            Ogre::ColourValue get_pixel( int i_x, int i_y );

            /**
                @brief Gets the width of the layer
                @return Width in pixels or rooms
            */
            inline int get_width() const {
                return m_image.getWidth();
            }

            /**
                @brief Gets the height of the layer
                @return Height in pixels or rooms
            */
            inline int get_height() const {
                return m_image.getHeight();
            }

            /**
                @brief Gets the ID of the layer
                @return Layer ID
            */
            inline int get_id() const {
                return m_id;
            }

            /**
                @brief Updates the map
            */
            void update();

            /**
                @brief Loads the layer image
                @param[in] r_path Reference to the path of the image
                @param[in] r_group Reference to the resource group
                @return True on success, false on failure
            */
            bool load_image( const std::string &r_path, const std::string &r_group );

            /**
                @brief Gets a room variable
                @param[in] i_x X-coordinate of the room
                @param[in] i_y Y-coordinate of the room
                @param[in] r_name Reference to the name of the variable
                @return Variable value
            */
            std::string get_room_var( int i_x, int i_y, const std::string &r_name );

            /**
                @brief Sets a room variable
                @param[in] i_x X-coordinate of the room
                @param[in] i_y Y-coordinate of the room
                @param[in] r_name Reference to the name of the variable
                @param[in] r_val Reference to the value of the variable
                @return True on success, false on failure
            */
            bool set_room_var( int i_x, int i_y, const std::string &r_name, const std::string &r_val );

            /**
                @brief Sets layer rendering mode
                @param[in] e_mode Rendering mode
            */
            void set_render_mode( pm3::RENDER_MODE e_mode );

            /**
                @brief Gets layer rendering mode
                @return Rendering mode
            */
            inline pm3::RENDER_MODE get_render_mode() const {
                return m_render_mode;
            }

            /**
                @brief Saves layer image As
                @param[in] r_path Image file path
                @return Path to the image on success, empty string on failure
            */
            std::string save_image( const std::string &r_path );

            /**
                @brief Saves layer image
                @return Path to the image on success, empty string on failure
            */
            inline std::string save_image() {
                return save_image( m_image_path );
            }

            /**
                @brief Saves layer script As
                @param[in] r_path Path to the script
                @return Path to the script on success, empty string on failure
            */
            std::string save_script( const std::string &r_path );

            /**
                @brief Saves layer script
                @return Path to the script on success, empty string on failure
            */
            inline std::string save_script() {
                return save_script( m_script_path );
            }

            /**
                @brief Gets layer image path
                @return Layer image path
            */
            inline std::string get_image_path() const {
                return m_image_path;
            }

            /**
                @brief Does the layer image have any unsaved changes?
                @return True if it does, otherwise false
            */
            inline bool is_image_unsaved() const {
                return m_image_unsaved;
            }

            /**
                @brief Shows or hides debug boxes
                @param[in] b_show To show or hide?
            */
            void show_debug_boxes( bool b_show );

            /**
                @brief Checks if debug boxes are shown
                @return True or false
            */
            inline bool debug_boxes_shown() const {
                return m_show_debug;
            }

            /**
                @brief Sets coordinate offset for room positions.
                @note Allows for negative coordinates, which are needed for backwards compatibility after resizing layer image
                @param[in] i_x Offset along the X-axis
                @param[in] i_y Offset along the Y-axis
            */
            void set_coord_offset( int i_x, int i_y );

            /**
                @brief Gets X-coordinate offset
                @return Offset
            */
            inline int get_x_offset() const {
                return m_offset_x;
            }

            /**
                @brief Gets Y-coordinate offset
                @return Offset
            */
            inline int get_y_offset() const {
                return m_offset_y;
            }

    protected:
            /**
                @brief Initializes the layer and its roommap
                @param[in] i_id Layer index (height in rooms or pixels)
            */
            void init( int i_id );

            /**
                @brief Binds functions called by lua
            */
            void bind();

            /**
                @brief Sets the color of a pixel
                @param[in] i_x X-coordinate of the pixel position
                @param[in] i_y Y-coordinate of the pixel position
                @param[in] r_color Reference to pixel color
            */
            void set_pixel( int i_x, int i_y, const Ogre::ColourValue &r_color );

            /**
                @brief Adds columns to the left (negative X-coordinates)
                @param[in] i_num Number of columns to be added
            */
            void add_columns_left( int i_num );

            /**
                @brief Adds columns to the right (positive X-coordinates)
                @param[in] i_num Number of columns to be added
            */
            void add_columns_right( int i_num );

            /**
                @brief Adds columns to the top (negative Y-coordinates)
                @param[in] i_num Number of rows to be added
            */
            void add_rows_top( int i_num );

            /**
                @brief Adds columns to the bottom (positive Y-coordinates)
                @param[in] i_num Number of rows to be added
            */
            void add_rows_bottom( int i_num );

            //! Map layer index
            int m_id;

            //! Map layer image
            IMAGE m_image;

            //! Map layer image filepath
            std::string m_image_path;

            //! X-coordinate offset
            int m_offset_x;
            //! Y-coordinate offset
            int m_offset_y;

            //! Map layer rooms
            std::list< std::list< PIXELROOM > > m_rooms;

            //! Layer rendering mode
            pm3::RENDER_MODE m_render_mode;
            //! Show debug boxes
            bool m_show_debug;

            //! Does layer image have any unsaved changes?
            bool m_image_unsaved;
    };

    namespace lua {
        //! Pointer to the currently active layer to which all lua functions apply
        extern PIXELLAYER *g_active_layer;
    }
}

#endif

